module dungeon.console;

import std.algorithm;
import std.stdio;
import std.format;
import std.conv;

import derelict.sdl.sdl;

import nade.ast;
import nade.loader;
import nade.time;
import nade.primitive;
import nade.color;
import nade.vector;
import nade.font;

import dungeon.game;


class Console {
	public {
		Game				game;
	}
	private {
		bool				_open = false;
		real				_position = 0;
		real				_desiredPosition = 0;
		real				_speed = 0;
		string[]		_lines;
		string			_commandLine;
	}

	this(Game game_)
	{
		game = game_;
	}

	void render()
	{
		if(_desiredPosition > _position)
		{
			_position = min(_desiredPosition, _position + _speed*frameTime);
		}
		else if(_desiredPosition < _position)
		{
			_position = max(_desiredPosition, _position - _speed*frameTime);
		}

		if(_position > 0)
		{
			Color.BLACK.render(0.75);
			renderFilledRectangle(Vector2(0, 768 - _position), Vector2(1024, 768));

			Color.WHITE.render;
			renderLine(Vector2(0, 768 - _position), Vector2(1024, 768 - _position));

			font.printFixed(8, 768 - _position + 8, "> "~_commandLine~((cast(int)(curTime*3)%2) == 0 ? "_" : ""), 7, 12, Color.LIGHT_YELLOW, Color.WHITE);

			int y = 768 - cast(int)_position + 20;
			foreach_reverse(string line;  _lines)
			{
				font.printFixed(8, y, line, 7, 12, Color.PALE_YELLOW);
				if((y += 12) < 0) break;
			}
		}
	}

	bool processEvent(SDL_Event event, out bool quit = false)
	{
		switch(event.type)
		{
			case SDL_KEYDOWN:
			{
				switch(event.key.keysym.sym)
				{
					case SDLK_F4:
					{
						return false;
					}

					case SDLK_BACKQUOTE:
					{
						close();
						break;
					}

					case SDLK_BACKSPACE:
					{
						if(_commandLine.length > 0) _commandLine = _commandLine[0 .. ($-1)];
						break;
					}

					case SDLK_RETURN:
					{
						if(_commandLine.length > 0)
						{
							writef(": %s", _commandLine);

							auto result = processCommand(_commandLine);
							if(result.quit) quit = true;

							_commandLine = "";
						}
						break;
					}

					default:
					{
						if(event.key.keysym.unicode != 0  && (event.key.keysym.unicode&0xFF80) == 0)
						{
							_commandLine ~= cast(char)(event.key.keysym.unicode&0x7F);
						}
					}
				}

				return true;
			}
			case SDL_QUIT:
			{
				return false;
			}

			default:
		}

		return true;
	}

	void open(real position, real speed = 1200)
	{
		_open = true;
		_desiredPosition = position;
		_speed = speed;
	}

	void close(real speed = 1200)
	{
		_open = false;
		_desiredPosition = 0;
		_speed = speed;
	}

	void toggle(real position, real speed = 1200)
	{
		if(_open)
			close(speed);
		else
			open(position, speed);
	}

	bool isOpen() { return _open; }

	CommandResult processCommand(string command)
	{
		auto ast = AST.parse("command ("~command~")");
		auto result = new CommandResult(this);
		Object[string] state = [ "console": this ];
		load(result, ast, state);
		return result;
	}

	void write(string message)
	{
		_lines ~= message;
	}

	void writef(...)
	{
		string message = "";
		void putc(dchar c) { message ~= c; }
		doFormat(&putc, _arguments, _argptr);

		write(message);
	}
}

class CommandResult {
	public {
		bool			quit = false;
		Console		console;
		Game			game;
	}

	this(Console console_)
	{
		console = console_;
		game = console.game;
	}
	

	static void install()
	{
		Loader!(CommandResult).add([ "quit", "exit" ],
			(ref CommandResult cmd, AST ast, Object[string] state) { cmd.quit = true; }
		);

		Loader!(CommandResult).add("echo",
			(ref CommandResult cmd, AST ast, Object[string] state)
			{
				foreach(node; ast) cmd.console.write(node.toString);
				cmd.console.write("");
			}
		);
	}
}
